{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "https://mp.weixin.qq.com/s?__biz=MzkyMDMxNDkxOA==&mid=2247484495&idx=1&sn=8f64fb590d7daeb10d908a43213252ae&chksm=c195f106f6e27810bf00120588fa8f7eb5ae953ea781964548e7de897a9c86bfb48ede8a0be1&scene=178&cur_album_id=3447886516519747585#rd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 振动升降指标ASI策略.py\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from datetime import datetime\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_hist_k_data(code,start_date,end_date,frequency='d')->pd.DataFrame:\n",
    "    \"\"\"\n",
    "    获取历史K线数据\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    import baostock as bs\n",
    "    bs.login()\n",
    "    rs = bs.query_history_k_data_plus(code,\"date,code,open,high,low,close,preclose,volume,amount,pctChg\",start_date,end_date,frequency=frequency)\n",
    "    data_list = []\n",
    "    while (rs.error_code == '0') & rs.next():\n",
    "        # 获取一条记录，将记录合并在一起\n",
    "        data_list.append(rs.get_row_data())\n",
    "    result = pd.DataFrame(data_list, columns=rs.fields)\n",
    "    result.open = result.open.astype(float)\n",
    "    result.high = result.high.astype(float)\n",
    "    result.low = result.low.astype(float)\n",
    "    result.close = result.close.astype(float)\n",
    "    result.date= pd.to_datetime(result.date)\n",
    "    result.set_index('date',inplace=True)\n",
    "    bs.logout()\n",
    "    return result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "login success!\n",
      "logout success!\n"
     ]
    }
   ],
   "source": [
    "# 获取股票数据\n",
    "stock_symbol = 'sz.000001'\n",
    "start_date = '2005-01-01'\n",
    "end_date = '2023-01-01'\n",
    "data = get_hist_k_data(stock_symbol, start_date, end_date)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Rolling' object has no attribute 'idxmax'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[7], line 12\u001b[0m\n\u001b[0;32m      9\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m data\n\u001b[0;32m     11\u001b[0m \u001b[38;5;66;03m# 生成交易信号\u001b[39;00m\n\u001b[1;32m---> 12\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[43mcalculate_aroon\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m     13\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mSignal\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[0;32m     14\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mPosition\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n",
      "Cell \u001b[1;32mIn[7], line 5\u001b[0m, in \u001b[0;36mcalculate_aroon\u001b[1;34m(data, period)\u001b[0m\n\u001b[0;32m      3\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhigh\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhigh\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mrolling(window\u001b[38;5;241m=\u001b[39mperiod)\u001b[38;5;241m.\u001b[39mmax()\n\u001b[0;32m      4\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlow\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlow\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mrolling(window\u001b[38;5;241m=\u001b[39mperiod)\u001b[38;5;241m.\u001b[39mmin()\n\u001b[1;32m----> 5\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDaysSinceHigh\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m period \u001b[38;5;241m-\u001b[39m (\u001b[43mdata\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mhigh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrolling\u001b[49m\u001b[43m(\u001b[49m\u001b[43mwindow\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mperiod\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43midxmax\u001b[49m() \u001b[38;5;241m-\u001b[39m data\u001b[38;5;241m.\u001b[39mindex)\u001b[38;5;241m.\u001b[39mdt\u001b[38;5;241m.\u001b[39mdays\n\u001b[0;32m      6\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDaysSinceLow\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m period \u001b[38;5;241m-\u001b[39m (data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlow\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;241m.\u001b[39mrolling(window\u001b[38;5;241m=\u001b[39mperiod)\u001b[38;5;241m.\u001b[39midxmin() \u001b[38;5;241m-\u001b[39m data\u001b[38;5;241m.\u001b[39mindex)\u001b[38;5;241m.\u001b[39mdt\u001b[38;5;241m.\u001b[39mdays\n\u001b[0;32m      7\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAroonUp\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m (period \u001b[38;5;241m-\u001b[39m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDaysSinceHigh\u001b[39m\u001b[38;5;124m'\u001b[39m]) \u001b[38;5;241m/\u001b[39m period \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m100\u001b[39m\n",
      "File \u001b[1;32mc:\\Users\\Dev\\.conda\\envs\\tf\\Lib\\site-packages\\pandas\\core\\window\\rolling.py:321\u001b[0m, in \u001b[0;36mBaseWindow.__getattr__\u001b[1;34m(self, attr)\u001b[0m\n\u001b[0;32m    318\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m attr \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mobj:\n\u001b[0;32m    319\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m[attr]\n\u001b[1;32m--> 321\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\n\u001b[0;32m    322\u001b[0m     \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m object has no attribute \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mattr\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    323\u001b[0m )\n",
      "\u001b[1;31mAttributeError\u001b[0m: 'Rolling' object has no attribute 'idxmax'"
     ]
    }
   ],
   "source": [
    "# 计算阿隆指标\n",
    "def calculate_aroon(data, period=25):\n",
    "    data['high'] = data['high'].rolling(window=period).max()\n",
    "    data['low'] = data['low'].rolling(window=period).min()\n",
    "    data['DaysSinceHigh'] = period - (data['high'].rolling(window=period).idxmax() - data.index).dt.days\n",
    "    data['DaysSinceLow'] = period - (data['low'].rolling(window=period).idxmin() - data.index).dt.days\n",
    "    data['AroonUp'] = (period - data['DaysSinceHigh']) / period * 100\n",
    "    data['AroonDown'] = (period - data['DaysSinceLow']) / period * 100\n",
    "    return data\n",
    "\n",
    "# 生成交易信号\n",
    "data = calculate_aroon(data)\n",
    "data['Signal'] = 0\n",
    "data['Position'] = 0\n",
    "\n",
    "# 当AroonUp从下方穿过AroonDown时买入\n",
    "data['Signal'][data['AroonUp'] > data['AroonDown']] = 1\n",
    "# 当AroonDown从下方穿过AroonUp时卖出\n",
    "data['Signal'][data['AroonDown'] > data['AroonUp']] = -1\n",
    "\n",
    "# 计算持仓\n",
    "data['Position'] = data['Signal'].diff()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# 回测策略\n",
    "def backtest_strategy(data):\n",
    "    data['Strategy_Returns'] = data['Position'].shift(1) * data['close'].pct_change()\n",
    "    data['Cumulative_Returns'] = (1 + data['Strategy_Returns']).cumprod()\n",
    "    return data\n",
    "\n",
    "data = backtest_strategy(data)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 绘制策略的累计收益\n",
    "plt.figure(figsize=(14, 7))\n",
    "plt.plot(data['Cumulative_Returns'], label='aroon Strategy')\n",
    "plt.title('aroon Strategy Cumulative Returns')\n",
    "plt.xlabel('Date')\n",
    "plt.ylabel('Cumulative Returns')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "tf",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
